import React from "react";
import {Button, Divider, Form, FormInstance, Input, Layout, Menu, message, Modal} from "antd";
import {
    fetchSysHosts,
    guid,
    Host,
    loadSettings,
    MessageType,
    Settings,
    writeHost2System,
    writeHostString2System,
    writeSetting2FileDebounce
} from "../common/utils";
import {ipcRenderer} from "electron";
import {EventKeys, Keyboard} from "../common/EventKeys";
import {hostsMode, LogoIcon, showMessage, showSuccessMsg, SystemIcon} from "./utils";
import _ from "lodash";
import StatusBar from "./components/StatusBar";
import {CheckOutlined, GlobalOutlined, PlusOutlined} from "@ant-design/icons";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/mode/shell/shell"

const {Item: MenuItem} = Menu;

ipcRenderer.send(EventKeys.initTrayMenu);

interface ApplicationState {
    dark: boolean
    settings: Settings
    current: string
    openGroups: string[]
    editing: Host
}

enum HostKeys {
    systemDefault = "system-default",
    currentHosts = "current-hosts"
}

function isCustomHost(key: string) {
    return ![HostKeys.currentHosts, HostKeys.systemDefault, "custom-hosts"].includes(key);
}

document.getElementById("root").addEventListener("dragstart", e => {
    e.preventDefault();
    if (e.dataTransfer.files.length !== 1) {
        message.error("一次只能拖放一个文件")
        return;
    }
    const file = e.dataTransfer.files.item(0);
    ipcRenderer.send(EventKeys.onDragStart, file.path)
});

export default class Application extends React.Component<any, ApplicationState> {
    private readonly form = React.createRef<FormInstance<Host>>();

    constructor(props) {
        super(props);
        this.state = {
            dark: false,
            settings: loadSettings(),
            current: HostKeys.currentHosts,
            editing: null,
            openGroups: ["other-group"]
        }
    }

    componentDidMount() {
        const html = document.getElementsByTagName("html")[0];
        ipcRenderer.on(EventKeys.themeChange, (event, dark) => {
            this.setState({dark});
            html.setAttribute("data-theme", dark ? "dark" : "light");
        });
        ipcRenderer.on(EventKeys.needReloadSettings, () => {
            this.setState({settings: loadSettings()});
        });
        ipcRenderer.on(EventKeys.changeHost, (event, host: Host) => {
            this.setState(
                {editing: host},
                () => this.form.current.setFieldsValue({name: host.name})
            )
        });
        ipcRenderer.on(EventKeys.applyHost, (event, host: Host) => {
            this.applyHosts(host)
        });
        ipcRenderer.on(EventKeys.deleteHost, (event, host: Host) => {
            if (host.isActive) {
                message.warn("当前规则已应用，无法删除！");
                return;
            }
            Modal.confirm({
                title: `确定要删除"${host.name}"吗？`,
                onOk: () => {
                    const settings = _.cloneDeep(this.state.settings);
                    Reflect.deleteProperty(settings.hosts, host.id);
                    this.setState({settings}, () => {
                        ipcRenderer.send(EventKeys.initTrayMenu, this.state.settings)
                    });
                }
            })
        });
        ipcRenderer.on(EventKeys.addNewRule, this.onAddNewRule);
        ipcRenderer.on(EventKeys.reset2default, this.reset2default)
    }

    onTreeClick = (info) => {
        this.setState({current: info.key});
    };

    applyHosts = (host: Host) => {
        writeHost2System(host).then((msg) => {
            const settings = _.cloneDeep(this.state.settings);
            settings.hosts[host.id] = {...host, isActive: !host.isActive};
            this.setState({settings, current: HostKeys.currentHosts});
            ipcRenderer.send(EventKeys.initTrayMenu, this.state.settings);
            showMessage(msg, MessageType.success);
        }).catch(reason => {
            showMessage(reason, MessageType.error);
        });
    }

    onHostsChange = (editor, data, value) => {
        const {current, settings} = this.state;
        this.setState({
            settings: {
                ...settings,
                hosts: {
                    ...settings.hosts,
                    [current]: {
                        ...settings.hosts[current],
                        lastUpdateTime: Date.now(),
                        content: value
                    }
                }
            },
        })
    }

    componentDidUpdate(prevProps: Readonly<any>, prevState: Readonly<ApplicationState>, snapshot?: any) {
        writeSetting2FileDebounce(this.state.settings)
    }

    getContent = () => {
        const {dark, settings, current} = this.state;
        if (isCustomHost(current)) {
            return settings.hosts[current]?.content || "";
        }
        if (current === HostKeys.systemDefault) {
            return settings.defaultHosts || "";
        }
        if (current === HostKeys.currentHosts) {
            return fetchSysHosts() || "";
        }
        return "";
    }

    onTreeRightClick = (host: Host) => {
        ipcRenderer.send(EventKeys.showTreeContextMenu, host)
    };

    onEditorContextMenu = () => {
        if (!isCustomHost(this.state.current)) return;
        ipcRenderer.send(EventKeys.showEditorContextMenu)
    };

    onInputBlur = (e, host: Host) => {
        const settings = _.cloneDeep(this.state.settings);
        const value = (e.target as HTMLInputElement).value;
        if (!value && !host.name) {
            delete settings.hosts[host.id];
            this.setState({settings, editing: null});
            return;
        }
        if (!value) {
            this.setState({settings: loadSettings(), editing: null});
            return;
        }

        settings.hosts[host.id] = {
            ...host,
            name: value
        }

        if (!host.name && !host.content) {
            settings.hosts[host.id].content = `# ${value} \n`;
        }

        this.setState({
            editing: null,
            settings,
            current: host.id
        }, () => {
            ipcRenderer.send(EventKeys.initTrayMenu, this.state.settings)
        })
    };

    onInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (e.key as Keyboard === Keyboard.Escape) {
            this.setState({
                editing: null,
                settings: loadSettings()
            })
        }
        if ([
            Keyboard.Backspace,
            Keyboard.Enter
        ].includes(e.key as Keyboard)) return;
        if (e.currentTarget.value.length >= 20) {
            e.preventDefault();
        }
    }

    reset2default = () => {
        Modal.confirm({
            title: "确定要恢复系统默认值吗？",
            content: "将恢复到第一次使用本软件之前的hosts",
            okText: "确定恢复",
            cancelText: "取消",
            onOk: () => {
                const {settings} = this.state;
                const res = writeHostString2System(settings.defaultHosts);
                if (res) {
                    showSuccessMsg("hosts已默认为系统默认");
                    const hosts = _.cloneDeep(settings.hosts);
                    Object.entries(hosts).forEach(([id, host]) => {
                        hosts[id] = {...host, isActive: false};
                    })
                    this.setState({
                        settings: {...settings, hosts},
                        current: HostKeys.currentHosts
                    }, () => {
                        ipcRenderer.send(EventKeys.initTrayMenu, this.state.settings);
                    });
                }
            }
        });
    };
    onEditModalCancel = () => {
        this.setState({editing: null}, () => this.form.current.resetFields())
    }
    onEditModalOk = () => {
        this.form.current.validateFields().then(values => {
            const {editing} = this.state;
            const settings = _.cloneDeep(this.state.settings);
            const now = Date.now();
            settings.hosts[editing.id] = {
                content: `# ${values.name}\n\n`,
                ...editing,
                ...values,
                lastUpdateTime: now
            };
            this.setState({
                settings, editing: null,
                current: editing.id,
            }, () => this.form.current.resetFields())
        })
    }
    onAddNewRule = () => {
        this.setState({
            editing: {
                id: guid(),
                name: "",
                isActive: false,
                createTime: Date.now(), content: ""
            }
        });
    };

    renderEditingInput = (host: Host) => {
        return (
            <Input
                key={`editing-input-${host.id}`}
                defaultValue={host.name}
                autoFocus
                maxLength={20}
                onKeyDown={this.onInputKeyDown}
                onBlur={(e) => this.onInputBlur(e, host)}
                onPressEnter={(e) => this.onInputBlur(e, host)}
            />
        );
    }

    renderHostItem = ([id, host]: [string, Host]) => {
        return (
            <MenuItem
                onDoubleClick={() => this.applyHosts(host)}
                key={id}
                data-active={host.isActive}
                icon={host.isActive ? <CheckOutlined/> : <GlobalOutlined/>}
                onContextMenu={() => this.onTreeRightClick(host)}
            >
                {host.name}
            </MenuItem>
        );
    }

    render() {
        const {dark, settings, current, editing, openGroups} = this.state;
        return (
            <>
                <div style={{display: 'flex', flexDirection: 'column', height: '100%'}}>
                    <Layout style={{height: '100%', width: '100%', flex: 1}}>
                        <Layout.Sider width={240}>
                            <div style={{display: "flex", flexDirection: "column", height: '100%'}}>
                                <div style={{flex: 1, overflow: "auto"}}>
                                    <Menu
                                        onClick={this.onTreeClick}
                                        selectable
                                        style={{width: 256}}
                                        openKeys={openGroups}
                                        selectedKeys={[current]}
                                        onOpenChange={keys => this.setState({openGroups: keys})}
                                        mode="inline"
                                    >
                                        <MenuItem icon={<LogoIcon/>} key={HostKeys.currentHosts}>
                                            当前生效
                                        </MenuItem>
                                        <MenuItem
                                            icon={<SystemIcon/>}
                                            key={HostKeys.systemDefault}
                                            onDoubleClick={this.reset2default}
                                        >
                                            系统默认
                                        </MenuItem>
                                        <Menu.ItemGroup title="自定义 Hosts">
                                            {Object.entries(settings.hosts).map(this.renderHostItem)}
                                        </Menu.ItemGroup>
                                    </Menu>
                                </div>
                                <Divider/>
                                <Button type="text" icon={<PlusOutlined/>} onClick={this.onAddNewRule}>
                                    新建规则
                                </Button>
                            </div>
                        </Layout.Sider>
                        <Layout.Content>
                            <CodeMirror
                                defineMode={hostsMode}
                                onContextMenu={this.onEditorContextMenu}
                                value={this.getContent()}
                                onBeforeChange={this.onHostsChange}
                                options={{
                                    theme: dark ? '3024-night' : 'default',
                                    mode: 'hosts',
                                    lineNumbers: true,
                                    lineWrapping: true,
                                    readOnly: !isCustomHost(current)
                                }}
                            />
                        </Layout.Content>
                    </Layout>
                    <StatusBar/>
                </div>
                <Modal
                    title={editing?.name ? "修改 Host 规则" : "新建 Host 规则"}
                    visible={!!editing}
                    onCancel={this.onEditModalCancel}
                    onOk={this.onEditModalOk}
                    width={400}
                    cancelText="取消"
                    okText="确定"
                >
                    <Form
                        layout="vertical"
                        ref={this.form}
                        initialValues={{name: ""}}
                    >
                        <Form.Item
                            required
                            name="name"
                            rules={[
                                {required: true, message: "请输入规则名称"}
                            ]}
                        >
                            <Input placeholder="请输入规则名称" allowClear maxLength={20} autoFocus/>
                        </Form.Item>
                    </Form>
                </Modal>
            </>
        );
    }
}